from flask import Flask, request, render_template, render_template_string, flash, make_response
from get_data import get_data, adjust_args, get_weekly_data
import webbrowser
from pathlib import Path
from flask_paginate import Pagination
from PIL import Image
from modifying_img import get_modifying_args
import psutil
import json
import logging
from threading import Thread
from time import sleep

app = Flask(__name__)
my_static = app.root_path / Path('static')

log = logging.getLogger('werkzeug')
log.setLevel(logging.WARNING)
app.logger.handlers = log.handlers

UPLOAD_FOLDER = my_static / '.temp_bgimg'
if not UPLOAD_FOLDER.exists():
    UPLOAD_FOLDER.mkdir()
ALLOWED_MIMETYPE = {'image/png', 'image/jpeg'}

app.config['MAX_CONTENT_LENGTH'] = 12 * 1024 * 1024
# app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER

img_list = [str(p.relative_to(app.root_path))
            for p in my_static.glob('img/*')]

form_data = {'juan1': '传道书', 'zhang1': 3, 'capacity1': 2,
             'juan2': '雅各书', 'zhang2': 5, 'capacity2': 1, 'date': '2024-12-16'}


@app.route('/', methods=['GET', 'POST'])
def root_entry():

    if request.method == 'GET':
        return render_template('form_page.min.html')
    elif request.method == 'POST':
        form_data.update(request.form.to_dict())
        # 为什么不直接本地保存form_data, 而要从get_data返回?
        # 防止"祷告事项"的第一天不是"周一"
        data, to_store = get_data(form_data)
        resp = make_response(render_template(
            'weekly_schedule.min.html', datas=data))
        # 从Chrome 104开始, cookie的最长保存期限是34560000 seconds (400 days)
        resp.set_cookie('wenli', json.dumps(to_store),
                        max_age=34560000, httponly=True)
        return resp


@app.route('/get_form_data', methods=['GET'])
def get_form_data():
    # 从本地读取数据, 否则使用默认数据
    if wenli := request.cookies.get('wenli'):
        form_data.update(json.loads(wenli))
    if data := adjust_args(form_data):
        keys = ('juan1', 'zhang1', 'capacity1',
                'juan2', 'zhang2', 'capacity2', 'date')
        form_data.update(zip(keys, data))
    return form_data


@app.route('/get_weekly_form_data', methods=['POST'])
def reset_form_data():
    return get_weekly_data(request.json)


@app.route('/img/<int:img_index>')
def show_img(img_index):

    per_page = 10

    total = len(img_list)

    pages = abs(total // -per_page)

    start = (img_index-1) * per_page
    end = per_page * img_index

    if img_index > pages:
        img_index = pages

    pagination = Pagination(page=img_index, total=len(img_list), per_page=per_page,
                            bs_version=4, href='javascript:img_pagination({0})')
    pagination_html = render_template_string(
        '{{pagination.links}}', pagination=pagination)

    return {'imgs': img_list[start:end], 'pagination_html': pagination_html}


@app.route('/local-img', methods=['POST'])
def local_img():
    # check if the post request has the file part
    if 'file' not in request.files:
        flash('No file part')
        return ['warning', '没有图片被选择']
    f = request.files['file']
    # If the user does not select a file, the browser submits an
    # empty file without a filename.
    if f.filename == '':
        flash('No selected file')
        f.close()
        return ['warning', '没有图片被选择']

    if f and f.mimetype in ALLOWED_MIMETYPE:
        suffix = f.mimetype.removeprefix('image/')
        dest_locale = UPLOAD_FOLDER/f'temp_img.{suffix}'
        try:
            modifying(f, dest_locale)
        except Exception as e:
            print(e)
            return ['error', str(e)]

        return ['ok', str(dest_locale.relative_to(app.root_path))]


def modifying(f, dest_locale):
    aspect_ratio = 5/8
    target_w = 720
    with Image.open(f) as img:
        w, h = img.size
        img_args = get_modifying_args(
            w, h, target_w, target_h=None, aspect_ratio=aspect_ratio)
        if img_args:
            *crop_args, resize_w, resize_h = img_args
            img = img.resize((resize_w, resize_h), resample=Image.LANCZOS)
            img = img.crop(crop_args)

        img.save(dest_locale)


def run_as_singleton():
    current_p = psutil.Process()
    current_p_name = current_p.name()
    current_p_pid = current_p.pid

    if current_p_name == 'python.exe':
        return

    for proc in psutil.process_iter(('pid', 'name')):
        p_info = proc.info

        if p_info.get('name') == current_p_name and p_info.get('pid') != current_p_pid:
            proc.kill()
            # 极端事例中, 在kill命令发出后尚未完成时get_available_port函数已产生新port
            sleep(0.5)


def get_available_port(candidate):
    port_generator = (i for i in range(2000, 10000))
    sconns = psutil.net_connections('tcp')
    while True:
        for conn in sconns:
            # if true, 端口占用
            if conn.status == 'LISTEN' and conn.laddr.port == candidate:
                break
        else:
            return candidate

        # 未try-catch
        # 极端情况下, next整个range都找不到可用port, 会报错
        candidate = next(port_generator)


def launch_browser(url):
    sleep(0.5)
    webbrowser.open(url)

    print('',
          ' *** Hi, friend from rongmei!',
          ' *** As long as this terminal window stay alive,',
          ' *** you can access the link below',
          ' *** through a Chromium-based web browser at any time.',
          f' *** running on {url}',
          '',
          sep='\n')


if __name__ == '__main__':
    run_as_singleton()
    host = '127.0.0.1'
    port = get_available_port(5000)
    url = f'http://{host}:{port}'

    Thread(target=launch_browser, args=(url,)).start()

    app.run(host, port)
